﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using App1AppHandler;
using System.IO;
using System.Xml;
using System.Net.Sockets;
using System.Data.SqlClient;
using Reborn_WorldServer;
using System.Reflection;
using Reborn_WorldServer.App1AppHandler.Spells.Interfaces;

namespace Reborn_Server.App1AppHandler
{
    public class BuffManager
    {
        private CreatureBase myCreatureBase;
        private List<Buff> _Buffs = new List<Buff>();

        //Constuctor, sets reference to controlling creatureBase
        public BuffManager(CreatureBase creatureBase)
        {
            myCreatureBase = creatureBase;
        }


        //Storage for buffs on OTHER mobs casted by this creature. 
        //These buffs require a reference to the casting creature (this). If the casting creature gets removed
        //from the server, these buffs must be removed from their targets.
        private List<Buff> _tiedBuffs = new List<Buff>();
        public void addTiedBuff(Buff buff)
        {
            lock (_tiedBuffs)
            {
                //Since tied buffs require reference to caster AND target, dont allow them to save.
                buff.saveBuff = false;

                _tiedBuffs.Add(buff);
            }
        }
        public void removeTiedBuff(Buff buff)
        {
            lock (_tiedBuffs)
            {
                _tiedBuffs.Remove(buff);
            }
        }
        public void cancelAllTiedBuffs()
        {
            lock (_tiedBuffs)
            {
                while (_tiedBuffs.Count > 0)
                {
                    Buff buff = _tiedBuffs[0];
                    buff.RemoveSpell(true, true);
                    _tiedBuffs.Remove(buff);
                }
            }
        }


        //Thread safe remove buff (This should only be called by the buff itself which will do the clean up)
        public void RemoveBuff(Buff buff)
        {
            lock (_Buffs)
            {
                //We need a try catch because sometimes we return a copy list of buffs so 
                //Multiple threads may attempt to remove the same buff
                try
                {
                    _Buffs.Remove(buff);
                }
                catch { }
            }
        }

        //Thread safe add buff
        public void AddBuff(Buff buff)
        {
            lock (_Buffs)
            {
                _Buffs.Add(buff);
            }
        }

        //Sends update if creaturebase is Character
        public void UpdateBuffs()
        {
            lock (_Buffs)
            {
                if (myCreatureBase is Character)
                {
                    Character myCharacter = (Character)myCreatureBase;

                    try
                    {
                        string result = "";

                        StringWriter stringWriter = new StringWriter();
                        XmlTextWriter xmldoc = new XmlTextWriter(stringWriter);

                        xmldoc.WriteStartElement("buffs");

                        foreach (Buff spell in _Buffs)
                        {
                            xmldoc.WriteStartElement("buff");
                            xmldoc.WriteAttributeString("SpellID", spell.SpellID.ToString());
                            xmldoc.WriteAttributeString("Minutes", spell.minutesLeft.ToString());
                            xmldoc.WriteRaw(spell.spellIcon.toXML());
                            xmldoc.WriteEndElement();
                        }

                        xmldoc.WriteEndElement();
                        xmldoc.Flush();

                        xmldoc.Close();

                        stringWriter.Flush();

                        result = stringWriter.ToString();

                        myCharacter.ClientSocket.socket.BeginSend(result);
                    }
                    catch { }
                }
            }
        }

        //Night Vision Override Incase creature has innate night vision it does not need the buff
        public bool HasInnateNightVision = false;

        //Convience search of buffs to find night vision
        public bool HasNightVision()
        {
            if (HasInnateNightVision == true)
                return true;

            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff.spellIcon.Name == "Night Vision")
                        return true;
                }
            }

            return false;
        }

        public bool hasBuff(long spellGUID)
        {
            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff.SpellID == spellGUID)
                        return true;
                }
            }
            return false;
        }

        public bool HasBreatheWater()
        {
            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff.spellIcon.Name == "Breathe Water")
                        return true;
                }
            }
            return false;
        }

        public bool HasFeatherFall()
        {
            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff.spellIcon.Name == "Feather Fall")
                        return true;
                }
            }
            return false;
        }

        public IAIBuff GetAIBuff()
        {
            lock (_Buffs)
            {
                Buff buff = GetBuff("Fear");
                if (buff != null)
                    return (IAIBuff)buff;

                buff = GetBuff("Enslave");
                if (buff != null)
                    return (IAIBuff)buff;
            }

            return null;
        }

        public void runAndRemoveDeathEffectBuffs()
        {
            lock (_Buffs)
            {
                for (int x = 0; x < _Buffs.Count; x++)
                {
                    Buff buff = _Buffs[x];
                    if (buff is IDeathEffect)
                    {
                        IDeathEffect deathEffect = (IDeathEffect)buff;
                        deathEffect.runDeathEffect();
                        buff.RemoveSpell(false, false);
                        x--;
                    }
                }
            }
        }

        public IDamageAbsorbBuff GetDamageAbsorbBuff()
        {
            IDamageAbsorbBuff result = null;
            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff is IDamageAbsorbBuff)
                    {
                        IDamageAbsorbBuff daBuff = (IDamageAbsorbBuff)buff;
                        if (result == null || result.BuffPriority() > daBuff.BuffPriority())
                            result = daBuff;
                    }
                }
            }

            return result;
        }

        public bool HasHide()
        {
            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff.spellIcon.Name == "Hide")
                        return true;
                }
            }
            return false;
        }

        //Convience search of buffs to see if creature is stunned
        public bool IsStunned()
        {
            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff.spellIcon.Name.ToLower().IndexOf("stun") > -1 || buff.spellIcon.Name == "Lightning" || buff.spellIcon.Name == "Mesmerize")
                        return true;
                    else if ((buff.spellIcon.StackType == "FreezingAura" && buff.spellIcon.Offensive == true) || buff.spellIcon.StackType == "Mesmerize" || buff.spellIcon.StackType == "Stun" || buff.spellIcon.StackType == "Web" || buff.spellIcon.StackType == "Lightning")
                        return true;
                }
            }
            return false;
        }

        //Convience search of buffs to see if creature is stunned
        public bool IsFeared()
        {
            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff.spellIcon.Name == "Fear")
                        return true;
                }
            }
            return false;
        }

        //Convience search of buffs to see if creature is silenced
        public bool IsSilenced()
        {
            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff.spellIcon.Name == "Silence")
                        return true;
                }
            }
            return false;
        }

        public string IsRooted()
        {
            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff is IRootBuff)
                    {
                        IRootBuff rootBuff = (IRootBuff)buff;
                        return rootBuff.RootMessage();
                    }
                }
            }
            return "";
        }

        public Buff GetBuff(long SpellID)
        {
            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff.SpellID == SpellID)
                        return buff;
                }
            }

            return null;
        }
        public Buff GetBuff(string name)
        {
            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff.spellIcon.Name == name)
                        return buff;
                }
            }
            return null;
        }
        public Buff GetBuffByStackType(string stackType)
        {
            lock (_Buffs)
            {
                foreach (Buff buff in _Buffs)
                {
                    if (buff.spellIcon.StackType != "" && buff.spellIcon.StackType == stackType)
                        return buff;
                }
            }
            return null;
        }


        //Returns a copy of the buffs list (Thready safety) we cant inumerate the main list
        //because it may get modified by a different thread
        public List<Buff> Buffs
        {
            get
            {
                List<Buff> buffs = new List<Buff>();
                lock (_Buffs)
                {
                    foreach (Buff buff in _Buffs)
                    {
                        buffs.Add(buff);
                    }
                }
                return buffs;
            }
        }

        public void SaveBuffs()
        {
            SqlConnection MyCon = new SqlConnection(Managers.DBConnectionString);
            SqlCommand SQLCom = new SqlCommand("Characters_Buffs_DeletePerCharacterID", MyCon);
            SQLCom.CommandType = System.Data.CommandType.StoredProcedure;
            SQLCom.Parameters.AddWithValue("@CharacterID", myCreatureBase.ID);

            MyCon.Open();
            SQLCom.ExecuteNonQuery();

            //MyCon.Close();
            //MyCon.Dispose();

            lock (_Buffs)
            {
                SQLCom = new SqlCommand("Characters_Buffs_Insert", MyCon);
                SQLCom.CommandType = System.Data.CommandType.StoredProcedure;

                //MyCon.Open();

                foreach (Buff buff in _Buffs)
                {
                    if (buff is Spell_Charm)
                        continue;

                    int timeRemaining = 0;
                    if (buff.minutesLeft > 0)
                        timeRemaining = buff.minutesLeft;
                    else if (buff.ticksLeft > 0)
                        timeRemaining = buff.ticksLeft;
                    else if (buff.minutesLeft == -1)
                        timeRemaining = -1;

                    if (timeRemaining == 0 || buff.spellIcon.ID_DB == -1 || buff.saveBuff == false)
                        continue;

                    SQLCom.Parameters.AddWithValue("@CharacterID", myCreatureBase.ID);
                    SQLCom.Parameters.AddWithValue("@BuffID", buff.spellIcon.ID_DB);
                    SQLCom.Parameters.AddWithValue("@TimeRemaining", timeRemaining);
                    SQLCom.Parameters.AddWithValue("@CasterCharacterID", buff.CastingCharacterID);
                    SQLCom.Parameters.AddWithValue("@CasterInt", buff.CastingCreatureInt);
                    SQLCom.Parameters.AddWithValue("@CasterWis", buff.CastingCreatureWis);
                    SQLCom.Parameters.AddWithValue("@CasterLevel", buff.CastingCreatureLevel);
                    SQLCom.Parameters.AddWithValue("@OptionalValue", buff.GetOptionalValue());

                    string xmlData = buff.GetXmlData();
                    if (xmlData != "")
                    {
                        SQLCom.Parameters.Add("@XmlData", System.Data.SqlDbType.Xml);
                        SQLCom.Parameters["@XmlData"].Value = xmlData;
                    }

                    SQLCom.ExecuteNonQuery();
                    SQLCom.Parameters.Clear();

                }
            }

            MyCon.Close();
            MyCon.Dispose();
        }

        public void ResumeBuffs()
        {
            SqlConnection MyCon = new SqlConnection(Managers.DBConnectionString);
            SqlCommand SQLCom = new SqlCommand("Characters_Buffs_Select", MyCon);
            SQLCom.CommandType = System.Data.CommandType.StoredProcedure;
            SQLCom.Parameters.AddWithValue("@CharacterID", this.myCreatureBase.ID);

            MyCon.Open();
            SqlDataReader SDR = SQLCom.ExecuteReader();

            LockItem lockItem = Managers.GameHandler.GetLockItem(myCreatureBase.MapID, myCreatureBase.Instance);

            while (SDR.Read())
            {
                SpellIcon spellIcon = Managers.GameHandler.Spells[int.Parse(SDR["BuffID"].ToString())];

                Assembly assembly = Assembly.GetExecutingAssembly();
                string fullTypeName = "Reborn_Server.App1AppHandler" + "." + spellIcon.ClassName;
                Type myType = assembly.GetType(fullTypeName);

                object ClassObj = Activator.CreateInstance(myType);

                int TimeRemaining =  int.Parse(SDR["TimeRemaining"].ToString());
                int CasterCharacterID = int.Parse(SDR["CasterCharacterID"].ToString());
                int CasterInt = int.Parse(SDR["CasterInt"].ToString());
                int CasterWis = int.Parse(SDR["CasterWis"].ToString());
                int CasterLevel = int.Parse(SDR["CasterLevel"].ToString());
                int OptionalValue = int.Parse(SDR["OptionalValue"].ToString());
                string xmlData = SDR["XmlData"] == DBNull.Value ? null : SDR["XmlData"].ToString();

                //override public void Resume(CreatureBase targetCreatureBase, SpellIcon _spellIcon, int timeRemaining, int castLevel, int castCharacterID, int castInt, int castWis, int optionalValue)
                Object[] args = { this.myCreatureBase, spellIcon, TimeRemaining, CasterLevel, CasterCharacterID, CasterInt, CasterWis, OptionalValue, xmlData };

                lock (lockItem.LockObj)
                {
                    object Result = myType.InvokeMember("Resume", BindingFlags.Default | BindingFlags.InvokeMethod, null, ClassObj, args);
                }
            }

            SDR.Close();
            SDR.Dispose();

            MyCon.Close();
            MyCon.Dispose();

            UpdateBuffs();
        }
    }
}
